// 编译器
// https://www.cnblogs.com/tugenhua0707/p/7589602.html
// https://segmentfault.com/a/1190000016434836?utm_source=tag-newest
// https://www.jianshu.com/p/3b581af02ebf
function Compile(el, vm) {
  this.vm = vm;
  this.el = document.querySelector(el);
  this.fragment = null;
  this.init();
}

Compile.prototype = {
  init() {
    if (this.el) {
      this.fragment = this.node2Fragment(this.el);
      this.compileElement(this.fragment);
      this.el.appendChild(this.fragment);
    } else {
      console.warn("dom元素不存在");
    }
  },
  node2Fragment(el) {
    const fragment = document.createDocumentFragment();
    let child = el.firstChild;
    while (child) {
      fragment.appendChild(child);
      child = el.firstChild;
    }
    return fragment;
  },
  compileElement(el) {
    const childNodes = el.childNodes;
    // 遍历所有子节点 元素节点 文本节点
    Array.from(childNodes).forEach(node => {
      const text = node.textContent;
      const reg = /\{\{(.*)\}\}/;
      if (this.isElementNode(node)) {
        // 元素节点
        this.compile(node);
      } else if (this.isTextNode(node) && reg.test(text)) {
        // 文本节点
        this.compileText(node, reg.exec(text)[1]);
      }
      // 递归子元素
      if (node.childNodes && node.childNodes.length) {
        this.compileElement(node);
      }
    });
  },
  compile(node) {
    // 获取节点所有属性
    const nodeAttrs = node.attributes;
    // 判断是事件还是指令
    Array.from(nodeAttrs).forEach(attr => {
      const attrName = attr.name;
      // 首先判断属性名v-开头
      if (this.isDirective(attrName)) {
        const exp = attr.value;
        // 获取v-xx中字符串
        const dir = attrName.substring(2);
        if (this.isEventDirective(dir)) {
          // 事件指令
          this.compileEvent(node, this.vm, exp, dir);
        } else {
          // v-model指令
          this.compileModel(node, this.vm, exp, dir);
        }
        node.removeAttribute(attrName);
      }
    });
  },
  compileText(node, exp) {
    const initText = this.vm.data[exp];
    this.updateText(node, initText);
    new Watcher(this.vm, exp, value => {
      this.updateText(node, value);
    });
  },
  // 编译on事件
  compileEvent(node, vm, exp, dir) {
    const eventType = dir.split(":")[1];
    const cb = vm.methods && vm.methods[exp];
    if (eventType && cb) {
      node.addEventListener(eventType, cb.bind(vm), false);
    }
  },
  // 编译v-model指令
  compileModel(node, vm, exp, dir) {
    let val = vm.data[exp];
    this.updateModel(node, val);
    new Watcher(vm, exp, value => {
      this.updateModel(node, value);
    });
    node.addEventListener("input", e => {
      const newVal = e.target.value;
      if (val === newVal) return;
      vm.data[exp] = newVal;
      val = newVal;
    });
  },
  // 更新文本
  updateText(node, value) {
    node.textContent = typeof value === "undefined" ? "" : value;
  },
  // 更新v-model
  updateModel(node, value, oldVal) {
    node.value = typeof value === "undefined" ? "" : value;
  },
  // 是否v-
  isDirective(attrName) {
    return attrName.indexOf("v-") === 0;
  },
  // 是否on:事件
  isEventDirective(dir) {
    return dir.indexOf("on:") === 0;
  },
  // 是否文本节点
  isTextNode(el) {
    return el.nodeType === 3;
  },
  // 是否dom元素节点
  isElementNode(el) {
    return el.nodeType === 1;
  }
};

// const data2 = {message: 'ni-hao2'}
// document.addEventListener('DOMContentLoaded', function () {
//   new Compile('#app', data2)
// })
